﻿using System;
using System.IO.IsolatedStorage;
using System.Runtime.Serialization;
using System.Collections.Generic;
using System.Linq;

using GeoLocationSharing.ViewModel;
using System.IO;
using System.Reflection;

namespace GeoLocationSharing.Model
{

    /// <summary>
    /// Handles Storage operations in IsolatedStorage
    /// </summary>
    public class InternalStore
    {
        /// <summary>
        /// Master File is serialized into XML file and stored in Isolated Storage folder
        /// </summary>
        public class MasterFile
        {
           public Dictionary<string, LocationViewModel> _locationCollection;
        }


        #region Variables

        IsolatedStorageFile _isoFile;
        IsolatedStorageFileStream _iStreamWriteMode;
        IsolatedStorageFileStream _iStreamReadMode;
        
        /// <summary>
        /// Static Reference of InternalStore
        /// </summary>
        // It has to be static, Otherwise it cannot be instantiated by GetIsolatedStorageInstanceReference() static method
        static InternalStore _model;

        /// <summary>
        /// Instance reference of MasterFile class which holds the deserilaized data from Master.xml
        /// </summary>
        MasterFile _master;

        #endregion

        #region Instance Management

        /// <summary>
        /// Private constructor for intializing the variables. The private access specifier
        /// prevents object creation outside the class.
        /// </summary>
        InternalStore()
        {
            _isoFile = IsolatedStorageFile.GetUserStoreForApplication();
            _iStreamWriteMode = new IsolatedStorageFileStream("Master.xml", FileMode.Create, _isoFile);

        }

        /// <summary>
        /// Static constructor to intialize the class object
        /// </summary>
        static InternalStore()
        {
            _model = new InternalStore();        
        }

        /// <summary>
        /// Gets the reference of the IsolatedStorageModel
        /// </summary>
        /// <returns>InternalStore</returns>
        static public InternalStore GetIsolatedStorageInstanceReference()
        {
            if (_model == null)
            {
                _model = new InternalStore();
            }
            return _model;
        }

        static public void Initilalize()
        {
            if (_model == null)
            {
                _model = new InternalStore();
            }
        }

        #endregion


        #region Query Store Handlers

        /// <summary>
        /// An iterator for querying the Location dataset
        /// </summary>
        /// <param name="Query">Search term</param>
        /// <returns>IEnumerable of LocationViewModel</returns>
        /// <remarks>This method is not efficient. This can be improved by recursively querying Result set</remarks>
        public IEnumerable<LocationViewModel> QueryStoreForLocation(String Query)
        {
            var QueryResult = from q in _master._locationCollection where q.Value.Name.Contains(Query) select q.Value;
            foreach (var item in QueryResult.ToList())
            {
                yield return item;
            }
        }

        #endregion


        #region Location Handle

        /// <summary>
        /// Adds a location object to the collection
        /// </summary>
        /// <param name="Location">Location Object</param>
        /// <param name="DuplicateNameHandler">Method to execute when duplicate Name is encountered. (If no delegate is
        /// passed on, the method fails silently)</param>
        public void AddLocations(LocationViewModel Location, Action DuplicateNameHandler = null, Action LocationNameInvalid=null)
        {
            //If value of Name variable is empty, then it is invalid. Since we cannot add the object without a key
            if(Location.Name!="")
            {
                if (LocationNameInvalid!=null)
                {
                    LocationNameInvalid();
                }
            }
            else if (!_master._locationCollection.ContainsKey(Location.Name))
            {
                _master._locationCollection.Add(Location.Name, Location);
                Commit();
                return;
            }
            else if (DuplicateNameHandler != null)
            {
                DuplicateNameHandler();
            }
        }

        /// <summary>
        /// Deletes the Location object from collection
        /// </summary>
        /// <param name="Name">Name of the Location</param>
        /// <param name="ObjectNotFoundHandler">Method to execute when no Location object matching the key Name is found.
        /// (If no delegate is passed the method fails silently)</param>
        public void DeleteLocation(string Name, Action ObjectNotFoundHandler = null)
        {
            if (!_master._locationCollection.ContainsKey(Name))
            {
                _master._locationCollection.Remove(Name);
                Commit();
                return;
            }
            else
            {
                if (ObjectNotFoundHandler != null)
                    ObjectNotFoundHandler();
            }
        }

        private void Commit()
        {
            Serializer<MasterFile>(_master, _iStreamWriteMode);
        }


        void Modify()
        {
        }

        #endregion


        /// <summary>
        /// Serializes the Object and stores it in custom Format.
        /// </summary>
        /// <remarks>Generics is adapted to support extensibilty i.e. To support serialization for other objects</remarks>
        /// <typeparam name="T">MasterFile</typeparam>
        /// <param name="Object"></param>
        void Serializer<T>(T Object, Stream OutputStream) where T : MasterFile
        {
            if (typeof(T) ==typeof(MasterFile))
            {
                string _format = "$Nam:{0};Add:{1};Lat:{2};Lon:{3};TimeStamp:{4};$";
                if (Object == null)
                {
                    return;
                }
                string _value;
                foreach (var item in Object._locationCollection)
                {
                    _value = string.Format(_format, item.Value.Name,
                        item.Value.PhysicalAddress, item.Value.Latitude, item.Value.Longitude, item.Value.TimeStamp);
                    StreamWriter writer = new StreamWriter(OutputStream);
                    writer.Write(_value);
                    OutputStream.Flush();
                }
                
            }
        }

        T Deserializer<T>(Stream InputStream) where T : MasterFile
        {
            if (typeof(T) == typeof(MasterFile))
            {
                StreamReader _reader = new StreamReader(InputStream);
                return (T)(new MasterFile());
            }
            return default(T);
        }

    }
}
